﻿using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;

using UMC.Net;

namespace UMC.Proxy
{

    public class HttpMimeSocket : HttpMime
    {
        public override string Scheme => "http";

        public HttpMimeSocket(Socket socket, String ip)
        {
            this._socket = socket;
            this.ActiveTime = UMC.Data.Utility.TimeSpan();

            this.pid = socket.GetHashCode();

            this._Host = "127.0.0.1";

            this._remoteIpAddress = ip;

            HttpMimeServier.httpMimes.TryAdd(pid, this);
            SocketAsyncEventArgs eventArgs = new SocketAsyncEventArgs();

            eventArgs.SetBuffer(_data);
            eventArgs.Completed += Http;
            if (!this._socket.ReceiveAsync(eventArgs))
            {
                ProcessReceive(eventArgs);
            }
        }
        Socket _socket;
        int pid = 0;
        public override int Id => pid;

        String _remoteIpAddress, _Host;
        public override String Host => _Host;
        public override String RemoteIpAddress => _remoteIpAddress;

        public override void Write(byte[] buffer, int offset, int count)
        {
            if (isDispose == false)
            {
                try
                {

                    _socket.Send(buffer, offset, count, SocketFlags.None);
                }
                catch
                {
                    this.Dispose();
                }
            }
        }
        // UMC.Net.MimeRequest request;
        private void Http(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
            }
        }
        bool IsHeader = true;
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                if (e.BytesTransferred > 0)
                {
                    this.ActiveTime = UMC.Data.Utility.TimeSpan();
                    try
                    {
                        if (this.Request == null)
                        {
                            if (IsHeader)
                            {
                                IsHeader = true;
                                if (this._data[0] == 0x1)
                                {
                                    try
                                    {
                                        Subscribe(this._data, e.Offset + 1, e.BytesTransferred - 1);
                                        // _socket = null;
                                    }
                                    catch //(Exception ex)
                                    {

                                    }
                                    finally
                                    {
                                        this.Dispose();
                                    }
                                    return;
                                }
                            }
                            this.Request = new HttpMimeRequest(this);
                        }

                        var req = this.Request;
                        // req.Receive(this._data, 0, size);

                        req.Receive(this._data, e.Offset, e.BytesTransferred);

                        if (req.IsHttpFormatError)
                        {
                            this.Dispose();
                        }
                        else if (this.isDispose == false && !_socket.ReceiveAsync(e))
                        {
                            ProcessReceive(e);
                        }
                    }
                    catch (Exception ex)
                    {
                        this.OutText(500, ex.ToString());

                        return;
                    }
                }
            }
            else
            {
                this.Dispose();

            }
        }


        void Subscribe(byte[] header, int offset, int size)
        {
            var headers = new NameValueCollection();
            var ps = System.Text.Encoding.UTF8.GetString(header, offset, size).Split("\r\n");
            foreach (var s in ps)
            {
                var vi = s.IndexOf(':');
                var key = s.Substring(0, vi);
                var value = s.Substring(vi + 2);
                headers.Add(key, value);

            }

            int length = 0;
            var _subscribe = UMC.Net.NetSubscribe.Subscribe(headers, this._remoteIpAddress, HttpMimeServier.Server, _socket, UMC.Data.WebResource.Instance().Provider["appSecret"]);
            if (_subscribe != null)
            {

                length += $"HTTP/1.1 101 {HttpStatusDescription.Get(101)}\r\n".WriteBytes(header, length);
                length += "Connection: upgrade\r\n".WriteBytes(header, length);
                length += "Upgrade: websocket\r\n".WriteBytes(header, length);
                length += $"UMC-Publisher-Key: {HttpMimeServier.Server}\r\n".WriteBytes(header, length);
                length += "Server: Apiumc\r\n\r\n".WriteBytes(header, length);
                _socket.Send(header, 0, length, SocketFlags.None);
                _subscribe.Publish();
                _socket = null;
            }
            else
            {
                var text = "数据订阅连接验证不通过";
                length += $"HTTP/1.1 401 {HttpStatusDescription.Get(401)}\r\n".WriteBytes(header, length);
                length += $"Content-Type: text/plain; charset=utf-8\r\n".WriteBytes(header, length);
                length += $"Content-Length: {System.Text.Encoding.UTF8.GetByteCount(text)}\r\n".WriteBytes(header, length);
                length += "Connection: close\r\n".WriteBytes(header, length);
                length += "Server: Apiumc\r\n\r\n".WriteBytes(header, length);
                length += text.WriteBytes(header, length);

                _socket.Send(header, 0, length, SocketFlags.None);

            }
        }
        bool isDispose = false;
        public override void Dispose()
        {
            if (isDispose == false)
            {
                if (this._data != null)
                {
                    System.Buffers.ArrayPool<byte>.Shared.Return(this._data);
                    _data = null;
                }
                isDispose = true;
                try
                {
                    if (this.Request is IDisposable)
                    {
                        ((IDisposable)this.Request).Dispose();
                    }
                    _socket?.Shutdown(SocketShutdown.Both);
                }
                catch
                {

                }
                _socket?.Close();
            }
            HttpMimeServier.httpMimes.TryRemove(pid, out var _);

        }
        byte[] _data = System.Buffers.ArrayPool<byte>.Shared.Rent(0x600);
    }
}

